
; ACK v2 NesEncoder decompression routines
; by Carl Ådahl 2010-03-24
;
; Thanks to Tokumaru and the other NesDev gurus for ideas and guidance.
;
; 
;

	;variables in page 0
	.enum BaseMemory0

ACK_InputData 		.dsb 2 ; pointer to the data being decompressed
BankCount 		.dsb 1 ; number of banks in input
BankSize  		.dsb 2 ; uncompressed size of bank to decode
GroupCounter		.dsb 1
GroupHeader		.dsb 3
PatternCounter		.dsb 1
PatternOffset		.dsb 2
CommonPatternA		.dsb 8
CommonPatternB		.dsb 8
TempPattern		.dsb 8
OutputPos		.dsb 2
Temp			.dsb 1

	.ende

GROUPS_PER_BANK = 64 
PATTERNS_PER_GROUP = 8

OUTPUT_BYTES_PER_PATTERN = 8
OUTPUT_BYTES_PER_GROUP = 64

MACRO ReadByte_A
	lda (ACK_InputData),y
	iny
	bne +	
	inc ACK_InputData+1	; if y wrapped to 0 we increase the high byte of the ptr
+	
ENDM

MACRO SetOutputPPUAddress
	lda OutputPos+1
	sta $2006
	lda OutputPos
	sta $2006
ENDM


ACK_Decompress:
	ldy #0

	lda #0
	sta OutputPos
	sta OutputPos+1

	;jsr Huffman_Init
	ReadByte_A
	sta BankCount

	ReadByte_A
        sta BankSize
	ReadByte_A
	sta BankSize+1

	; Read "common patterns" A and B into ZP temporary buffers.
	ReadByte_A		; read RCR header byte
	asl			; C = Vertical flag (NOT USED HERE)
	sta Temp		; save for use while decoding

	ReadByte_A
	sta CommonPatternA+7

	ldx #6
-	asl Temp		; C = 1 to read, 0 to reuse last
	bcc +			 
	ReadByte_A
+	sta CommonPatternA,x
	dex
	bpl -

	ReadByte_A		; read RCR header byte
	asl			; C = Vertical flag (NOT USED HERE)
	sta Temp		; save for use while decoding

	ReadByte_A
	sta CommonPatternB+7

	ldx #6
-	asl Temp		; C = 1 to read, 0 to reuse last
	bcc +			 
	ReadByte_A
+	sta CommonPatternB,x
	dex
	bpl -
 
	
	SetOutputPPUAddress

	; Now decode!
	lda #GROUPS_PER_BANK
	sta GroupCounter

BeginDecodeGroup:

	ReadByte_A
	sta GroupHeader
	ReadByte_A
	sta GroupHeader+1
	ReadByte_A
	sta GroupHeader+2

	; Decode the patterns now.
	lda #PATTERNS_PER_GROUP-1
	sta PatternCounter

BeginPattern:
	lda #0
	asl GroupHeader
	rol
	asl GroupHeader+1
	rol
	asl GroupHeader+2
	rol

	; Use the RTS trick to jump to a routine handling the mode 
	asl 
	tax
	lda PatternJumpTable+1,x
	pha
	lda PatternJumpTable,x
	pha
	rts
PatternJumpTable: dc.w PMode0-1,PMode1-1,PMode2-1,PMode3-1,PMode4-1,PMode5-1,PMode6-1,PMode7-1

PMode0: ; Mode 0: Copy 8 bytes from input
	ldx #7
-	ReadByte_A
	sta $2007
	dex
	bpl -
	jmp EndPattern

PMode1: ; Mode 1: Row/Column Repeat
	ReadByte_A		; read RCR header byte
	asl			; C = Vertical flag
	sta Temp		; save for use while decoding
	bcs Vertical

Horizontal:
	ReadByte_A		; read line 0 byte and write it to VRAM
	sta $2007		

	ldx #6
-	asl Temp		; C = 1 to read, 0 to reuse last
	bcc +			 
	ReadByte_A		; A = read line from input
+	sta $2007		; write A to VRAM (new or reused)
	dex
	bpl -

	jmp EndPattern

Vertical:
	; This is trickier. Have to transpose the pattern so 
	; that columns become rows and the other way around.
	ReadByte_A		
	sta TempPattern+7	

	ldx #6
-	asl Temp		
	bcc +			 
	ReadByte_A
+	sta TempPattern,x
	dex
	bpl -

	; Now do the transpose.
	ldx #8			
-	
	i = 7
	REPT 8
	rol TempPattern+i	; next bit from line 'i' into carry..
	rol 			; roll into A
	i = i -1
	ENDR			; and so on for all eight.

	sta $2007		; then write the assembled line
	dex
	bne -

	jmp EndPattern

PMode2:	; Mode 2: Copy common pattern A
	ldx #7
-	lda CommonPatternA,x
	sta $2007
	dex
	bpl -
	jmp EndPattern

PMode3:	; Mode 3: Copy common pattern B
	ldx #7
-	lda CommonPatternB,x
	sta $2007
	dex
	bpl -
	jmp EndPattern

PMode4: 
	; Mode 4: Copy from one tile back (reading back from VRAM, via TempPattern)
	lda #0			; don't invert
	beq PMode45
PMode5:
	; Mode 5: Copy from one tile back but inverted
	lda #1			; do invert
PMode45:
	pha			; save invert flag

	; 16-bit subtraction: OutputPos-8 -> $2006 upper/lower
	sec		
	lda OutputPos
	sbc #8
	tax 
	lda OutputPos+1
	sbc #0
	sta $2006
	stx $2006
	lda $2007

	ldx #7
-	lda $2007
	sta TempPattern,x
	dex
	bpl -

	SetOutputPPUAddress

	ldx #7	

	pla			; restore invert flag and do what's appropriate
	bne +invert

-	lda TempPattern,x
	sta $2007
	dex
	bpl -
	jmp EndPattern

+invert
-	lda TempPattern,x
	eor #$FF
	sta $2007
	dex
	bpl -
	jmp EndPattern

PMode6:	
	; Mode 6: Copy of reference pattern + differing rows
	lda #0			; don't y-flip
	beq PMode67
PMode7:	
	; Mode 7: Copy of y-flipped reference pattern + differing rows
	lda #1			; do y-flip
PMode67:
	pha			; save y-flip flag

	lda #0
	sta PatternOffset+1	

	ReadByte_A 		; load header byte 1
	pha			; store it for later
	and #$7F		; extract reference pattern offset
	;clc
	;adc #1

	; Multiply by 8 and roll the carry bits into high byte
	asl
	rol PatternOffset+1
	asl
	rol PatternOffset+1
	asl
	rol PatternOffset+1
	sta PatternOffset	; Low byte is done too

	; Subtract the offset now from OutputPos, AND set up the PPU for reading
	; from the address.
	sec			
	lda OutputPos		
	sbc PatternOffset
	tax
	lda OutputPos+1
	sbc PatternOffset+1
	sta $2006
	stx $2006
	lda $2007		; read a junk byte (PPU pipelining somehow)

	pla			; load header byte 1 again 
	bmi +xflip		; is bit 7 set? then we must x-flip the ref.
;noxflip
	ldx #7
-	lda $2007
	sta TempPattern,x
	dex
	bpl -
	jmp +end
+xflip
	ldx #7
-	lda $2007
	REPT 8
	asl
	ror TempPattern,x
	ENDR
	dex
	bpl -
+end

	ReadByte_A 		; load header byte 2 (which rows are different)
	sta Temp  		
	
	SetOutputPPUAddress

	pla			; restore y-flip flag and do what's appropriate.
	bne +yflip

;+noyflip
	ldx #7
-	asl Temp		; C = 1 to read, 0 to reuse last
	lda TempPattern,x
	bcc +			 
	ReadByte_A		; A = read line from input
+	sta $2007		; write A to VRAM (new or reused)
	dex
	bpl -
	jmp EndPattern

+yflip
	ldx #0
-	asl Temp		; C = 1 to read, 0 to reuse last
	lda TempPattern,x
	bcc +			 
	ReadByte_A		; A = read line from input
+	sta $2007		; write A to VRAM (new or reused)
	inx
	cpx #8
	bne -

EndPattern:
	; Advance OutputPos to the next pattern (+8 bytes)
	lda OutputPos		
	clc
	adc #OUTPUT_BYTES_PER_PATTERN	
	sta OutputPos
	lda OutputPos+1
	adc #0
	sta OutputPos+1

	; Either do the next pattern or end the group now.
	dec PatternCounter
	bmi EndGroup
	jmp BeginPattern

EndGroup:
	dec GroupCounter
	beq +
	jmp BeginDecodeGroup
+
	rts

